﻿using System;
using System.IO;
using System.Xml.Linq;
using VsxFactory.Modeling.VisualStudio;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.TextManager.Interop;
using System.Text;

namespace VsxFactory.Modeling.Strategies.Rules
{
    /// <summary>
    /// A rule to manage xaml file
    /// </summary>
    public abstract class XamlRule : NodeGenerationRule
    {
        protected readonly XNamespace xns;

        public XamlRule()
            : this(null) {
        }

        public XamlRule(Type melType = null, string outputFileName = "{Name}.xaml")
            : base(melType, outputFileName) {
            OverwriteFile = false;
            xns = "http://schemas.microsoft.com/winfx/2006/xaml";
        }

        protected abstract void GenerateElements(IStrategyContext context, GenerationSpec element, XElement doc);
        /// <summary>
        /// Override this method 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="element"></param>
        /// <param name="doc"></param>
        /// <param name="oldName"></param>
        protected abstract void RemoveElements(IStrategyContext context, GenerationSpec element, XElement doc, string oldName);

        public override bool Generate(IStrategyContext context, GenerationSpec element, IVsProjectFileItem node) {
            if (base.Generate(context, element, node)) {
                var doc = LoadXaml(Node);
                RemoveElements(context, element, doc, null);
                GenerateElements(context, element, doc);
                SaveXaml(doc, Node);
                return true;
            }
            return false;
        }

        public override void Remove(IStrategyContext context, GenerationSpec wrapper) {
            var node = GetCurrentNode(context, wrapper);
            if (node != null) {
                var doc = LoadXaml(node);
                RemoveElements(context, wrapper, doc, null);
                SaveXaml(doc, node);
            }
        }

        /// <summary>
        /// A change is basicly a RemoveElement following by an AddElement
        /// </summary>
        /// <param name="context"></param>
        /// <param name="candidateElement"></param>
        /// <param name="targetElement"></param>
        /// <param name="propertyId"></param>
        /// <param name="oldValue"></param>
        public override void Change(IStrategyContext context, GenerationSpec candidateElement, GenerationSpec targetElement, Guid propertyId, object oldValue) {
            if (base.Generate(context, targetElement, null)) {
                var doc = LoadXaml(Node);
                string oldName = null;
                if (propertyId == candidateElement.NamePropertyId)
                    oldName = oldValue as string;
                RemoveElements(context, targetElement, doc, oldName);
                GenerateElements(context, targetElement, doc);
                SaveXaml(doc, Node);
            }
        }

        protected static XElement LoadXaml(IVsProjectFileItem node) {
            string text = node.GetContent();
            return System.Xml.Linq.XElement.Parse(text);
        }

        protected void SaveXaml(XElement doc, IVsProjectFileItem node) {
            StringWriter sw = new StringWriter();
            doc.Save(sw);
            string result = sw.ToString();
            int pos = result.IndexOf("?>");
            if (pos > 0)
                result = result.Substring(pos + 2);
            node.ReplaceText(result, Encoding.Unicode);
        }
    }
}
